From: Jonas Dreßler Date: Tue, 7 May 2024 17:07:06 +0000 (+0200) Subject: Add a ClutterGesture subclass for starting DND operations X-Git-Tag: archive/raspbian/50.5-1+rpi1~1^2~104^2^2~35 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=cb77339610ed3b94c3dc9fc3f25054f7b0a2598c;p=gnome-shell.git Add a ClutterGesture subclass for starting DND operations Factor out the starting part of the DND operation from dnd.js and move it into a new ClutterGesture subclass: ShellDndStartGesture As with the previous port to ClutterGesture, this new gesture is written in C due to performance concerns when we'll get rid of motion event compression and start handling eg. 1000 HZ mouse events at the full rate. This gesture now integrates with the ClutterGesture state tracking system (giving us the benefit of coordinating with other gestures), and will recognize (and also complete) as soon as a drag on a draggable actor should start. As soon as that happens, the existing code in dnd.js takes over, aquiring a (more exclusive) seat grab and handling things from there. This porting of dnd.js will be done in the next commit. Part-of: --- diff --git a/js/ui/swipeTracker.js b/js/ui/swipeTracker.js index f95574c3b..82edef29e 100644 --- a/js/ui/swipeTracker.js +++ b/js/ui/swipeTracker.js @@ -574,7 +574,7 @@ export const SwipeTracker = GObject.registerClass({ } _updatePanGesture(panGesture) { - const deltaVec = panGesture.get_delta(); + const deltaVec = panGesture.get_delta_abs(); const delta = this.orientation === Clutter.Orientation.HORIZONTAL ? -deltaVec.get_x() : -deltaVec.get_y(); diff --git a/src/st/meson.build b/src/st/meson.build index 5279455d8..5fb742b25 100644 --- a/src/st/meson.build +++ b/src/st/meson.build @@ -6,6 +6,7 @@ st_headers = [ 'st-box-layout.h', 'st-button.h', 'st-clipboard.h', + 'st-dnd-start-gesture.h', 'st-drawing-area.h', 'st-entry.h', 'st-focus-manager.h', @@ -119,6 +120,7 @@ st_sources = [ 'st-box-layout.c', 'st-button.c', 'st-clipboard.c', + 'st-dnd-start-gesture.c', 'st-drawing-area.c', 'st-entry.c', 'st-focus-manager.c', diff --git a/src/st/st-dnd-start-gesture.c b/src/st/st-dnd-start-gesture.c new file mode 100644 index 000000000..0e830e023 --- /dev/null +++ b/src/st/st-dnd-start-gesture.c @@ -0,0 +1,381 @@ +#include "config.h" + +#include + +#include "st-dnd-start-gesture.h" + +#include "st-theme-context.h" +#include "st-settings.h" + +typedef struct _StDndStartGesture StDndStartGesture; +typedef struct _StDndStartGesturePrivate StDndStartGesturePrivate; + +struct _StDndStartGesture +{ + ClutterGesture parent; +}; + +struct _StDndStartGesturePrivate +{ + gboolean drag_threshold_ignored; + ClutterEvent *point_begin_event; + ClutterEvent *drag_triggering_event; + + gboolean manual_mode; + guint32 timeout_threshold_ms; +}; + +enum +{ + PROP_0, + + PROP_MANUAL_MODE, + PROP_TIMEOUT_THRESHOLD, + + PROP_LAST +}; + +static GParamSpec *obj_props[PROP_LAST] = { NULL, }; + +G_DEFINE_TYPE_WITH_PRIVATE (StDndStartGesture, st_dnd_start_gesture, + CLUTTER_TYPE_GESTURE); + +static gboolean +should_handle_sequence (ClutterGesture *gesture, + const ClutterEvent *sequence_begin_event) +{ + ClutterEventType event_type = clutter_event_type (sequence_begin_event); + + if (event_type == CLUTTER_BUTTON_PRESS || + event_type == CLUTTER_TOUCH_BEGIN) + return TRUE; + + return FALSE; +} + +static void +maybe_start_drag (StDndStartGesture *self, + unsigned int point) +{ + StDndStartGesturePrivate *priv = + st_dnd_start_gesture_get_instance_private (self); + const ClutterEvent *event; + graphene_point_t begin_coords, coords; + int drag_threshold; + ClutterStage *stage; + StThemeContext *theme_context; + + event = clutter_gesture_get_point_event (CLUTTER_GESTURE (self), point); + + clutter_gesture_get_point_begin_coords_abs (CLUTTER_GESTURE (self), point, &begin_coords); + clutter_gesture_get_point_coords_abs (CLUTTER_GESTURE (self), point, &coords); + + if (priv->drag_threshold_ignored) + return; + + g_object_get (G_OBJECT (st_settings_get ()), + "drag-threshold", &drag_threshold, + NULL); + + stage = CLUTTER_STAGE (clutter_actor_get_stage (clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (self)))); + theme_context = st_theme_context_get_for_stage (stage); + drag_threshold *= st_theme_context_get_scale_factor (theme_context); + + if (ABS (coords.x - begin_coords.x) > drag_threshold || + ABS (coords.y - begin_coords.y) > drag_threshold) + { + ClutterInputDeviceType device_type; + gboolean is_pointer_or_touchpad; + guint32 ellapsed_time; + + device_type = + clutter_input_device_get_device_type (clutter_event_get_source_device (event)); + is_pointer_or_touchpad = + device_type == CLUTTER_POINTER_DEVICE || + device_type == CLUTTER_TOUCHPAD_DEVICE; + ellapsed_time = + clutter_event_get_time (event) - clutter_event_get_time (priv->point_begin_event); + + /* Pointer devices (e.g. mouse) start the drag immediately */ + if (is_pointer_or_touchpad || ellapsed_time > priv->timeout_threshold_ms) + st_dnd_start_gesture_start_drag (self, event); + else + priv->drag_threshold_ignored = true; + } +} + +static void +point_began (ClutterGesture *gesture, + unsigned int point) +{ + StDndStartGesture *self = ST_DND_START_GESTURE (gesture); + StDndStartGesturePrivate *priv = + st_dnd_start_gesture_get_instance_private (self); + const ClutterEvent *event; + + if (clutter_gesture_get_n_points (gesture) > 1) + { + clutter_gesture_set_state (gesture, CLUTTER_GESTURE_STATE_CANCELLED); + return; + } + + event = clutter_gesture_get_point_event (gesture, point); + + priv->point_begin_event = clutter_event_copy (event); + priv->drag_threshold_ignored = FALSE; + + if (!priv->manual_mode && + clutter_gesture_get_state (gesture) == CLUTTER_GESTURE_STATE_POSSIBLE) + maybe_start_drag (self, point); +} + +static void +point_moved (ClutterGesture *gesture, + unsigned int point) +{ + StDndStartGesture *self = ST_DND_START_GESTURE (gesture); + StDndStartGesturePrivate *priv = + st_dnd_start_gesture_get_instance_private (self); + + if (!priv->manual_mode && + clutter_gesture_get_state (gesture) == CLUTTER_GESTURE_STATE_POSSIBLE) + maybe_start_drag (self, point); +} + +static void +point_ended (ClutterGesture *gesture, + unsigned int point) +{ + if (clutter_gesture_get_state (gesture) == CLUTTER_GESTURE_STATE_POSSIBLE && + clutter_gesture_get_n_points (gesture) == 1) + { + /* All points were removed and we're still in POSSIBLE, this means + * we're in manual mode and nobody told us to start the drag. + */ + clutter_gesture_set_state (gesture, CLUTTER_GESTURE_STATE_CANCELLED); + } +} + +static void +state_changed (ClutterGesture *gesture, + ClutterGestureState old_state, + ClutterGestureState new_state) +{ + StDndStartGesture *self = ST_DND_START_GESTURE (gesture); + StDndStartGesturePrivate *priv = + st_dnd_start_gesture_get_instance_private (self); + + if (new_state == CLUTTER_GESTURE_STATE_WAITING) + { + g_clear_pointer (&priv->drag_triggering_event, clutter_event_free); + g_clear_pointer (&priv->point_begin_event, clutter_event_free); + } +} + +static void +st_dnd_start_gesture_set_property (GObject *gobject, + unsigned int prop_id, + const GValue *value, + GParamSpec *pspec) +{ + StDndStartGesture *self = ST_DND_START_GESTURE (gobject); + + switch (prop_id) + { + case PROP_MANUAL_MODE: + st_dnd_start_gesture_set_manual_mode (self, g_value_get_boolean (value)); + break; + + case PROP_TIMEOUT_THRESHOLD: + st_dnd_start_gesture_set_timeout_threshold (self, g_value_get_uint (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +st_dnd_start_gesture_get_property (GObject *gobject, + unsigned int prop_id, + GValue *value, + GParamSpec *pspec) +{ + StDndStartGesture *self = ST_DND_START_GESTURE (gobject); + + switch (prop_id) + { + case PROP_MANUAL_MODE: + g_value_set_boolean (value, st_dnd_start_gesture_get_manual_mode (self)); + break; + + case PROP_TIMEOUT_THRESHOLD: + g_value_set_uint (value, st_dnd_start_gesture_get_timeout_threshold (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +st_dnd_start_gesture_init (StDndStartGesture *self) +{ +} + +static void +st_dnd_start_gesture_class_init (StDndStartGestureClass *klass) +{ + ClutterGestureClass *gesture_class = CLUTTER_GESTURE_CLASS (klass); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gesture_class->should_handle_sequence = should_handle_sequence; + gesture_class->point_began = point_began; + gesture_class->point_moved = point_moved; + gesture_class->point_ended = point_ended; + gesture_class->state_changed = state_changed; + + gobject_class->set_property = st_dnd_start_gesture_set_property; + gobject_class->get_property = st_dnd_start_gesture_get_property; + + obj_props[PROP_MANUAL_MODE] = + g_param_spec_boolean ("manual-mode", NULL, NULL, + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_EXPLICIT_NOTIFY); + + obj_props[PROP_TIMEOUT_THRESHOLD] = + g_param_spec_uint ("timeout-threshold", NULL, NULL, + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_EXPLICIT_NOTIFY); + + g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); +} + +void +st_dnd_start_gesture_start_drag (StDndStartGesture *self, + const ClutterEvent *start_event) +{ + StDndStartGesturePrivate *priv; + + g_return_if_fail (ST_IS_DND_START_GESTURE (self)); + + priv = st_dnd_start_gesture_get_instance_private (self); + + if (clutter_gesture_get_n_points (CLUTTER_GESTURE (self)) != 1) + return; + + if (clutter_gesture_get_state (CLUTTER_GESTURE (self)) == CLUTTER_GESTURE_STATE_POSSIBLE) + { + if (start_event) + priv->drag_triggering_event = clutter_event_copy (start_event); + + clutter_gesture_set_state (CLUTTER_GESTURE (self), CLUTTER_GESTURE_STATE_COMPLETED); + } +} + +/** + * st_dnd_start_gesture_get_point_begin_event: + * @self: The gesture. + * + * Returns the first #ClutterEvent of the dragging sequence. + * + * Returns: (transfer none): A #ClutterEvent + */ +const ClutterEvent * +st_dnd_start_gesture_get_point_begin_event (StDndStartGesture *self) +{ + StDndStartGesturePrivate *priv; + + g_return_val_if_fail (ST_IS_DND_START_GESTURE (self), NULL); + + priv = st_dnd_start_gesture_get_instance_private (self); + + return priv->point_begin_event; +} + +/** + * st_dnd_start_gesture_get_drag_triggering_event: + * @self: The gesture. + * + * Returns the #ClutterEvent of the dragging sequence that actually triggered + * the drag. + * + * Returns: (transfer none): A #ClutterEvent + */ +const ClutterEvent * +st_dnd_start_gesture_get_drag_triggering_event (StDndStartGesture *self) +{ + StDndStartGesturePrivate *priv; + + g_return_val_if_fail (ST_IS_DND_START_GESTURE (self), NULL); + + priv = st_dnd_start_gesture_get_instance_private (self); + + return priv->drag_triggering_event; +} + +gboolean +st_dnd_start_gesture_get_manual_mode (StDndStartGesture *self) +{ + StDndStartGesturePrivate *priv; + + g_return_val_if_fail (ST_IS_DND_START_GESTURE (self), FALSE); + + priv = st_dnd_start_gesture_get_instance_private (self); + + return priv->manual_mode; +} + +void +st_dnd_start_gesture_set_manual_mode (StDndStartGesture *self, + gboolean manual_mode) +{ + StDndStartGesturePrivate *priv; + + g_return_if_fail (ST_IS_DND_START_GESTURE (self)); + + priv = st_dnd_start_gesture_get_instance_private (self); + + if (priv->manual_mode == manual_mode) + return; + + priv->manual_mode = manual_mode; + + g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MANUAL_MODE]); +} + +guint32 +st_dnd_start_gesture_get_timeout_threshold (StDndStartGesture *self) +{ + StDndStartGesturePrivate *priv; + + g_return_val_if_fail (ST_IS_DND_START_GESTURE (self), 0); + + priv = st_dnd_start_gesture_get_instance_private (self); + + return priv->timeout_threshold_ms; +} + +void +st_dnd_start_gesture_set_timeout_threshold (StDndStartGesture *self, + uint32_t timeout_threshold_ms) +{ + StDndStartGesturePrivate *priv; + + g_return_if_fail (ST_IS_DND_START_GESTURE (self)); + + priv = st_dnd_start_gesture_get_instance_private (self); + + if (priv->timeout_threshold_ms == timeout_threshold_ms) + return; + + priv->timeout_threshold_ms = timeout_threshold_ms; + + g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_TIMEOUT_THRESHOLD]); +} diff --git a/src/st/st-dnd-start-gesture.h b/src/st/st-dnd-start-gesture.h new file mode 100644 index 000000000..2e3cf804d --- /dev/null +++ b/src/st/st-dnd-start-gesture.h @@ -0,0 +1,37 @@ +#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) +#error "Only can be included directly.h" +#endif + +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +#define SHELL_TYPE_DND_START_GESTURE (st_dnd_start_gesture_get_type ()) +G_DECLARE_FINAL_TYPE (StDndStartGesture, st_dnd_start_gesture, + ST, DND_START_GESTURE, ClutterGesture) + +void st_dnd_start_gesture_start_drag (StDndStartGesture *self, + const ClutterEvent *start_event); + +void st_dnd_start_gesture_get_drag_coords (StDndStartGesture *self, + graphene_point_t *coords_out); + +const ClutterEvent * st_dnd_start_gesture_get_point_begin_event (StDndStartGesture *self); + +const ClutterEvent * st_dnd_start_gesture_get_drag_triggering_event (StDndStartGesture *self); + +gboolean st_dnd_start_gesture_get_manual_mode (StDndStartGesture *self); + +void st_dnd_start_gesture_set_manual_mode (StDndStartGesture *self, + gboolean manual_mode); + +guint32 st_dnd_start_gesture_get_timeout_threshold (StDndStartGesture *self); + +void st_dnd_start_gesture_set_timeout_threshold (StDndStartGesture *self, + uint32_t timeout_threshold_ms); + +G_END_DECLS